﻿/*
 * FlyObject.cs
 * 
 * Purpose:
 * Abstract class for any flying class.
 * 
 * Revision Log:
 * 06-SEP-12, Q. Nguyen, First Draft.
 * 08-SEP-12, C. Bush.
 * 13-SEP-12, Q. Nguyen
 */

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing.Drawing2D;
using System.Drawing;
using System.Threading;
using Air_Trafic_Control.Domain.Common;
using Air_Trafic_Control.Domain.Util;
using System.Diagnostics;
using System.Xml.Serialization;

namespace Air_Trafic_Control.Domain.Entity
{
    [Serializable()]
    [XmlInclude(typeof(C130))]
    [XmlInclude(typeof(F15))]
    [XmlInclude(typeof(UFO))]
    public abstract class FlyObject
    {
        #region Fields

        protected double m_posX = 0.0;
        protected double m_posY = 0.0;
        //double m_XDisplacement = 0.0;
        //double m_YDisplacement = 0.0;
        protected int m_Speed = 0;
        protected int m_Heading = 0;
        protected int m_TailNum = 0;
        protected Type m_FlyObjType;
        protected string m_FlyObjTypeName = "";
        protected Point m_pCenter = new Point();
        protected int m_rotateAdjustment = 90;// Constant here
        protected Point m_Target;
        protected Rectangle m_symRec;
        protected Timer m_FlyObjTimer;
        private static Logger log = new Logger(typeof(FlyObject));
        //private Stopwatch m_lastUpdate;
        private Random m_Randomizer = new Random();
        private bool m_IsSelected = false;
        protected long m_updateRate = 0L;
        private bool m_timerEnabled;

        #endregion

        #region Properties

        public int TailNumber
        {
            get
            {
                return m_TailNum;
            }
            set
            {
                this.m_TailNum = value;
            }
        }
       
        public int Heading
        {
            get 
            { 
                return m_Heading; 
            }
            set
            {
                this.m_Heading = value;
            }
        }

        public int Speed
        {
            get 
            { 
                return m_Speed; 
            }
            set 
            {
                this.m_Speed = value; 
            }
        }

        public Type FlyObjType
        {
            get
            {
                return this.m_FlyObjType;
            }
        }

        public string FlyObjTypeName
        {
            get { return m_FlyObjTypeName; }
            set { m_FlyObjTypeName = value; }
        }

        public Point OwnPosition
        {
            get
            {
                return this.m_pCenter;
            }
            set
            {
                this.m_pCenter = value;
            }
        }

        public Rectangle symRectangle
        {
            get { return m_symRec; }
           set
           {
              m_symRec = value;
           }
        }

        public Point Target
        {
            get { return m_Target; }
            set { m_Target = value; }
        }

        public double PosX
        {
            get
            {
                return this.m_posX;
            }
            set
            {
                this.m_posX = value;
            }
        }

        public double PosY
        {
            get
            {
                return this.m_posY;
            }
            set
            {
                this.m_posY = value;
            }
        }

        public int RotationAdjustment
        {
            get
            {
                return this.m_rotateAdjustment;
            }
            set
            {
                this.m_rotateAdjustment = value;
            }
        }

        public Boolean FlyObjTimerEnable
        {
            get { return this.m_timerEnabled; }
            set 
            {
                if (value)
                {
                    this.m_timerEnabled = true;
                    this.m_FlyObjTimer.Change(0, this.m_updateRate);
                }
                else
                {
                    this.m_timerEnabled = false;
                    this.m_FlyObjTimer.Change(Timeout.Infinite, Timeout.Infinite);
                }
            }
        }

        public bool IsSelected
        {
            get { return m_IsSelected; }
            set { m_IsSelected = value; }
        }

        #endregion

        #region Public fuctions

        /// <summary>
        /// Default constructor. This is only to allow serialization and should never be used.
        /// </summary>
        public FlyObject()
        {
             this.m_FlyObjTimer = new Timer(m_AircraftTimer_Tick);
        }

        public void FreeMoveTowardTarget()
        {
            double newDblX = 0.0, newDblY = 0.0, deltaDistance = 0.0;

            // Calculate the magnitude of the change in distance
            //deltaDistance = (this.Speed * this.m_lastUpdate.ElapsedMilliseconds) / 3600000.0; This does not stop flying object
            deltaDistance = (this.Speed * this.m_updateRate) / 3600000.0;

            // Convert to pixels on our RADAR screen
            deltaDistance *= Constants.PIXELS_PER_MILE;

            // Calculate the new Location
            newDblX = (deltaDistance * (Math.Cos(this.Heading * (Math.PI / 180)) * (180 / Math.PI))) + this.PosX;
            this.PosX = newDblX;
            //FlyObject.log.Info("New X: " + newDblX);

            newDblY = (deltaDistance * (Math.Sin(this.Heading * (Math.PI / 180)) * (180 / Math.PI))) + this.PosY;
            this.PosY = newDblY;
            //FlyObject.log.Info("New Y: " + newDblY);

            // Set the new Location
            this.OwnPosition = new Point((int)newDblX, (int)newDblY);
            this.m_symRec = new Rectangle(m_pCenter.X - 14, m_pCenter.Y - 14, 28, 28);

            // Restart the timer
            //this.m_lastUpdate.Restart();
        }

        public void InitPosRandomly()
        {
            Point newPoint = new Point();

            Random ran = new Random();
            double randomNumber = ran.NextDouble();
            int newY = 0, deltaX = 0, deltaY = 0;
            double airportAzimuth = 0.0;

            // Use the random number to get a random X position for new flyObj
            newPoint.X = (int)(randomNumber * (Constants.SYS_RADAR_DIAMETER + 1));

            // Calculate the Y position for the new flyObj where (Cx,Cy) is the center of the radar, r is the radius, 
            // x is the new X, and y is the Y
            // y = Cy + r ( sin( cos^( (Cx - x) / r) ) ) -- this is based on the parametric equation
            newY = (int)((double)Constants.SYS_RADAR_RADIUS * (Math.Sin(Math.Acos((double)(Constants.SYS_RADAR_CENTER - newPoint.X) / (double)Constants.SYS_RADAR_RADIUS))));

            // Randomly choose to place it above/below the center horizontal line
            if (((int)(randomNumber * 11) % 2) == 0)
            {
                newPoint.Y = Constants.SYS_RADAR_CENTER + newY;
            }
            else
            {
                newPoint.Y = Constants.SYS_RADAR_CENTER - newY;
            }

            // Set the new object's location
            this.OwnPosition = newPoint;
            this.PosX = (double)newPoint.X;
            this.PosY = (double)newPoint.Y;

            // Calculate a random heading +/- 45 deg of the azimuth to the airport
            deltaX = Constants.SYS_RADAR_CENTER - newPoint.X;
            deltaY = Constants.SYS_RADAR_CENTER - newPoint.Y;

            airportAzimuth = Math.Atan((double)deltaY / (double)deltaX) * (180.0 / Math.PI);

            if (((int)(randomNumber * 11) % 2) == 0)
            {
                this.Heading = (int)airportAzimuth + (int)(randomNumber * 35.0);
            }
            else
            {
                this.Heading = (int)airportAzimuth - (int)(randomNumber * 35.0);
            }

            // Adjust for reverse azimuth and set rotate adjustment
            if (this.OwnPosition.X > Constants.SYS_RADAR_CENTER)
            {
                this.Heading -= 180;
            }

            // Start the timer that will allow us to calculate disposition by using time and speed
            //this.m_lastUpdate = Stopwatch.StartNew();

            // Add the object to our list
            FlyObject.log.Info("Initialized new [" + this.FlyObjTypeName + "] at (" + newPoint.X + "," + newPoint.Y + ") with a heading of " + this.Heading + "deg!");
        }

        public int InitIDRandomly()
        {
           m_TailNum = m_Randomizer.Next(100);
           return m_TailNum;
        }

        public abstract void m_AircraftTimer_Tick(object state);

        #endregion

    }
}
